package opensky.dbcache.dbvp.core;

import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import opensky.dbcache.dbvp.api.FullTableName;
import opensky.dbcache.dbvp.api.MapType;
import opensky.dbcache.dbvp.api.PartionRule;
import opensky.dbcache.dbvp.api.RuleSet;
import opensky.dbcache.dbvp.rule.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * rule.xml配置加载类
 *
 * @Author zhenggm
 * @Date 2017/9/21 下午4:04
 */
public class ConfigLoad {

    private static Logger logger = LoggerFactory.getLogger(ConfigLoad.class);

    public ConfigLoad(String root) {
        this.init(root);
    }

    // 核心资源加载，解析xml生成RuleSet。
    public void init(String root) {

        Map<FullTableName, List<PartionRule>> fullEqualRules = new HashMap<>();// shcema和table都精确匹配的
        Map<String, List<PartionRule>> tableEqualRules = new HashMap<>();// table是精确匹配,schema是非精确匹配的规则列表
        Map<String, List<PartionRule>> schemaEqualRules = new HashMap<>();// schema是精确匹配,table是非精确匹配的规则列表
        List<PartionRule> mapRules = new ArrayList<>();// schema和table都是非精确匹配的规则列表


        // todo slf4j-simple配置删除
        logger.info("load rule.xml from {}", root);
        // 加载rule.xml文件，读取节点和属性
        DocumentBuilder db = null;
        DocumentBuilderFactory dbf = null;
        Element element = null;
        try {
            dbf = DocumentBuilderFactory.newInstance();
            db = dbf.newDocumentBuilder();
            Document dt = db.parse(root);
            // 得到一个根节点元素
            element = dt.getDocumentElement();
            // 获得子节点
            NodeList childNodes = element.getChildNodes();
            // 遍历所有的`table` 节点
            for (int i = 0; i < childNodes.getLength(); i++) {
                Node node = childNodes.item(i);
                Rule rule = null;
                List<PartionRule> partionRuleList = null;

                if ("table".equals(node.getNodeName())) {
                    rule = new Rule();
                    NodeList tableNode = node.getChildNodes();

                    for (int k = 0; k < tableNode.getLength(); k++) {
                        Node n = tableNode.item(k);

                        if ("schema".equals(n.getNodeName())) {
                            partionRuleList = new ArrayList<>();
                            NamedNodeMap attrs = n.getAttributes();
                            for (int j = 0; j < attrs.getLength(); j++) {
                                //通过item(index)方法获取book节点的某一个属性
                                Node attr = attrs.item(j);
                                if (attr.getNodeName().equals("type")) {
                                    rule.setSchemaType(MapType.getByValue(attr.getNodeValue()));
                                }
                            }
                            rule.setSchema(n.getTextContent());
                        }

                        if ("table".equals(n.getNodeName())) {
                            NamedNodeMap attrs = n.getAttributes();
                            for (int j = 0; j < attrs.getLength(); j++) {
                                //通过item(index)方法获取book节点的某一个属性
                                Node attr = attrs.item(j);
                                if (attr.getNodeName().equals("type")) {
                                    rule.setTableType(MapType.getByValue(attr.getNodeValue()));
                                }
                            }
                            rule.setTable(n.getTextContent());
                        }

                        if ("rule".equals(n.getNodeName())) {
                            NamedNodeMap attrs = n.getAttributes();
                            Map<String, Param> mapParam = new HashMap<>();
                            String schema = null;
                            String table = null;
                            for (int j = 0; j < attrs.getLength(); j++) {
                                //通过item(index)方法获取book节点的某一个属性
                                Node attr = attrs.item(j);
                                if (attr.getNodeName().equals("pass")) {
                                    rule.setProcess(Boolean.valueOf(attr.getNodeValue()));
                                }
                                if (attr.getNodeName().equals("schema")) {
                                    schema = attr.getNodeValue();
                                }
                                if (attr.getNodeName().equals("table")) {
                                    table = attr.getNodeValue();
                                }
                            }
                            FullTableName ftb = new FullTableName(schema, table);
                            rule.setFullTableName(ftb);

                            // 遍历param节点
                            NodeList p = n.getChildNodes();
                            for (int l = 0; l < p.getLength(); l++) {
                                Node paramNode = p.item(l);
                                NamedNodeMap paramNodeAttrs = paramNode.getAttributes();
                                String key = null;
                                Param param = new Param();

                                if (paramNode.getNodeName().equals("param")) {
                                    for (int m = 0; m < paramNodeAttrs.getLength(); m++) {
                                        Node paramAttr = paramNodeAttrs.item(m);
                                        if (paramAttr.getNodeName().equals("key")) {
                                            key = paramAttr.getNodeValue();
                                        }
                                        if (paramAttr.getNodeName().equals("type")) {
                                            param.setType(MatchType.getByValue(paramAttr.getNodeValue()));
                                        }
                                        if (paramAttr.getNodeName().equals("class")) {
                                            param.setCLASS(paramAttr.getNodeValue());
                                        }
                                        if (paramAttr.getNodeName().equals("format")) {
                                            param.setFormat(paramAttr.getNodeValue());
                                        }
                                    }
                                    param.setVaule(paramNode.getTextContent());
                                    // 存入rule中的map
                                    mapParam.put(key, param);
                                }
                            }

                            rule.setMap(mapParam);

                            Rule rule1 = new Rule();
                            rule1.setSchema(rule.getSchema());
                            rule1.setSchemaType(rule.getSchemaType());
                            rule1.setTable(rule.getTable());
                            rule1.setTableType(rule.getTableType());
                            rule1.setProcess(rule.isProcess());
                            rule1.setMap(rule.getMap());
                            rule1.setFullTableName(rule.getFullTableName());

                            // 将rule放入PartionRuleImpl实现类中
                            PartionRule partionRule = new PartionRuleImpl(rule1);
                            partionRuleList.add(partionRule);

                            if (rule.getSchemaType().equals(MapType.EQUAL) && rule.getTableType().equals(MapType.EQUAL)) {
                                FullTableName f = new FullTableName(rule.getSchema(), rule.getTable());
                                fullEqualRules.put(f, partionRuleList);
                            } else if (rule.getSchemaType().equals(MapType.REG) && rule.getTableType().equals(MapType.EQUAL)) {
                                tableEqualRules.put(rule.getTable(), partionRuleList);
                            } else if (rule.getSchemaType().equals(MapType.EQUAL) && rule.getTableType().equals(MapType.REG)) {
                                schemaEqualRules.put(rule.getSchema(), partionRuleList);
                            } else {
                                mapRules.add(partionRule);
                            }

                        }

                        // rule-custom 动态编译
                        if ("rule-custom".equals(n.getNodeName())) {
                            NamedNodeMap attrs = n.getAttributes();
                            Map<String, Param> mapParam = new HashMap<>();
                            String schema = null;
                            String table = null;
                            for (int j = 0; j < attrs.getLength(); j++) {
                                //通过item(index)方法获取book节点的某一个属性
                                Node attr = attrs.item(j);
                                if (attr.getNodeName().equals("pass")) {
                                    rule.setProcess(Boolean.valueOf(attr.getNodeValue()));
                                }
                                if (attr.getNodeName().equals("schema")) {
                                    schema = attr.getNodeValue();
                                }
                                if (attr.getNodeName().equals("table")) {
                                    table = attr.getNodeValue();
                                }
                            }
                            FullTableName ftb = new FullTableName(schema, table);
                            rule.setFullTableName(ftb);

                            // 获取shell脚本节点
                            NodeList p = n.getChildNodes();
                            String partionRuleShell = null;
                            for (int l = 0; l < p.getLength(); l++) {
                                Node paramNode = p.item(l);
                                if (paramNode.getNodeName().equals("shell")) {
                                    partionRuleShell = paramNode.getTextContent();// 获取脚本代码
                                }
                            }

                            Rule rule1 = new Rule();
                            rule1.setSchema(rule.getSchema());
                            rule1.setSchemaType(rule.getSchemaType());
                            rule1.setTable(rule.getTable());
                            rule1.setTableType(rule.getTableType());
                            rule1.setProcess(rule.isProcess());
                            rule1.setFullTableName(rule.getFullTableName());

                            // 进行动态编译
                            GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
                            Class<?> clazz = groovyClassLoader.parseClass(PartionString.getClassContext(partionRuleShell));
                            GroovyObject groovyObject = null;
                            try {
                                groovyObject = (GroovyObject) clazz.newInstance();
                            } catch (InstantiationException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                            groovyObject.invokeMethod("setRule", rule);// 初始化实现类中rule数据
                            // 转换成partionRule实现类
                            PartionRule partionRule = (PartionRule) groovyObject;
                            partionRuleList.add(partionRule);

                            if (rule.getSchemaType().equals(MapType.EQUAL) && rule.getTableType().equals(MapType.EQUAL)) {
                                FullTableName f = new FullTableName(rule.getSchema(), rule.getTable());
                                fullEqualRules.put(f, partionRuleList);
                            } else if (rule.getSchemaType().equals(MapType.REG) && rule.getTableType().equals(MapType.EQUAL)) {
                                tableEqualRules.put(rule.getTable(), partionRuleList);
                            } else if (rule.getSchemaType().equals(MapType.EQUAL) && rule.getTableType().equals(MapType.REG)) {
                                schemaEqualRules.put(rule.getSchema(), partionRuleList);
                            } else {
                                mapRules.add(partionRule);
                            }
                        }
                    }
                }
            }
            Global.RULESET = new RuleSet(fullEqualRules, tableEqualRules, schemaEqualRules, mapRules);
            logger.info("RULESET is build success!");
        } catch (ParserConfigurationException e) {
            logger.error(e.getMessage());
        } catch (SAXException e) {
            logger.error(e.getMessage());
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }
}
